#include "CSocket.h"
#include "CMemory.h"
#include "CConfigParser.h"
#include "CLogger.h"
#include "CLock.h"
#include "CProcessAttribute.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <stdio.h>
#include <errno.h>

CSocket::CSocket()
{
	m_nWorkerConnections = 1;			//epoll连接最大项数
	m_nListenPortCount = 1;				//监听一个端口
	m_nRecycleConnWaitTime = 60;		//等待60秒后才回收连接

	//epoll相关
	m_nEpollHandle = -1;

	//一些和网络通讯有关的常用变量值，供后续频繁使用时提高效率
	m_iPkgHeaderLen = sizeof(PkgHeader);
	m_iMsgHeaderLen = sizeof(MsgHeader);

	//各种队列相关
	m_iMsgSendCount = 0;				//发消息队列大小
	m_nTotalRecycleConnection = 0;		//待释放连接队列大小
	m_iTimerMapSize = 0;				//定时器队列大小
	m_lFirstTimer = 0;					//当前定时器队列头部的值
	m_iDiscardSendPkgCount = 0;			//丢弃的发送数据包数量

	m_iOnlineUserCount = 0;				//在线用户数量统计
	m_lLastPrintTime = 0;				//上次打印统计信息的时间
}

CSocket::~CSocket()
{
	//释放必须的内存
	//(1)监听端口相关内存的释放
	std::vector<lp_listening_t>::iterator pos;
	for (pos = m_vecListenSocket.begin(); pos != m_vecListenSocket.end(); ++pos)
	{
		delete (*pos);
	}
	m_vecListenSocket.clear();
}

//fork()之前做这个事情
bool CSocket::init()
{
	readConf();
	if (openListeningSockets() == false)
	{
		return false;
	}
	return true;
}

//子进程中需要执行的初始化函数
bool CSocket::initInChild()
{
	//发消息互斥量初始化
	if (pthread_mutex_init(&m_msgSendMutex, NULL) != 0)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中pthread_mutex_init(&m_msgSendMutex, NULL)失败。");
		return false;
	}

	//连接相关互斥量初始化
	if (pthread_mutex_init(&m_connectionMutex, NULL) != 0)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中pthread_mutex_init(&m_connectionMutex, NULL)失败。");
		return false;
	}

	//连接回收队列相关互斥量初始化
	if (pthread_mutex_init(&m_recycleMutex, NULL) != 0)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中pthread_mutex_init(&m_recycleMutex, NULL)失败。");
		return false;
	}

	//和时间处理队列有关的互斥量初始化
	if (pthread_mutex_init(&m_timerMutex, NULL) != 0)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中pthread_mutex_init(&m_timerMutex, NULL)失败。");
		return false;
	}

	//初始化发消息相关信号量
	if (sem_init(&m_semMsgSend, 0, 0) == -1)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中sem_init(&m_semMsgSend, 0, 0)失败。");
		return false;
	}

	//创建线程
	int iErr;
    ThreadItem* pMsgSend = new ThreadItem(this);	//专门用来发送数据的线程
    m_vecThread.push_back(pMsgSend);							//创建一个线程对象，并加入到容器中
	iErr = pthread_create(&pMsgSend->m_tHandle, NULL, sendDataThread, pMsgSend);	//创建线程
	if (iErr != 0)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中pthread_create(sendDataThread)失败。");
		return false;
	}

	//专门用来回收连接的线程
	ThreadItem* pRecycle = new ThreadItem(this);
	m_vecThread.push_back(pRecycle);
	iErr = pthread_create(&pRecycle->m_tHandle, NULL, recycleConnectionThread, pRecycle);
	if (iErr != 0)
	{
		CLogger::logStderr(0, "CSocket::initInChild()中pthread_create(recycleConnectionThread)失败。");
		return false;
	}

	if (m_bKickEnabled)		//是否开启踢人
	{
		ThreadItem* pKickMonitor = new ThreadItem(this);	//专门用来处理到期不发心跳包的用户踢出的线程
		m_vecThread.push_back(pKickMonitor);
		iErr = pthread_create(&pKickMonitor->m_tHandle, NULL, timerQueueMonitorThread, pKickMonitor);
		if (iErr != 0)
		{
			CLogger::logStderr(0, "CSocket::initInChild()中pthread_create(timerQueueMonitorThread)失败。");
			return false;
		}
	}

	return true;
}

//关闭退出函数[子进程执行]
void CSocket::shutdown()
{
	//把干活的线程停止掉，应该尝试通过设置 stopEvent = 1来让整个项目停止
	//用到信号量的，还需要调用一下sem_post
	if (sem_post(&m_semMsgSend) == -1)	//让sendDataThread流程走下来干活
	{
		CLogger::logStderr(0, "CSocket::shutdown()中sem_post(&m_semMsgSend)失败！");
	}

	std::vector<ThreadItem*>::iterator it;
	//等待线程终止
	for (it = m_vecThread.begin(); it != m_vecThread.end(); ++it)
	{
		pthread_join((*it)->m_tHandle, NULL);
	}

	//释放ThreadItem
	for (it = m_vecThread.begin(); it != m_vecThread.end(); ++it)
	{
		if (*it)
		{
			delete* it;
		}
	}
	m_vecThread.clear();

	//队列相关
	clearMsgSendQueue();
	clearConnection();
	clearAllFromTimerQueue();

	//多线程相关
	pthread_mutex_destroy(&m_connectionMutex);
	pthread_mutex_destroy(&m_msgSendMutex);
	pthread_mutex_destroy(&m_recycleMutex);
	pthread_mutex_destroy(&m_timerMutex);
	sem_destroy(&m_semMsgSend);
}

//打印统计信息
void CSocket::printStatisticsInfo()
{
	time_t lCurrTime = time(NULL);
	if ((lCurrTime - m_lLastPrintTime) > 10)
	{
		//超过10秒打印一次
		int nMsgRecvCount = g_clThreadPool.getMsgRecvCount();
		m_lLastPrintTime = lCurrTime;
		int iTmpOnlineUserCount = m_iOnlineUserCount;
		int iTmpSendQueueCount = m_iMsgSendCount;
		CLogger::logStderr(0, "---------------------begin-----------------");
		CLogger::logStderr(0, "当前在线人数/总人数(%d/%d)。", iTmpOnlineUserCount, m_nWorkerConnections);
		CLogger::logStderr(0, "当前连接池中空闲连接/总连接/要释放的连接(%d/%d/%d)。", m_lstFreeConnection.size(), m_lstConnection.size(), m_lstRecycleConnection.size());
		CLogger::logStderr(0, "当前时间队列大小(%d)。", m_mapTimer.size());
		CLogger::logStderr(0, "当前收消息队列/发消息队列大小分别为(%d/%d)，丢弃的待发送数据包数量为%d。", nMsgRecvCount, iTmpSendQueueCount, m_iDiscardSendPkgCount);
		if (iTmpSendQueueCount > 100000)
		{
			//接收队列过大，报一下，这个属于应该 引起警觉的，考虑限速等等手段
			CLogger::logStderr(0, "接收队列条目数量过大(%d)，要考虑限速或者增加处理线程数量了！", nMsgRecvCount);
		}
		CLogger::logStderr(0, "--------------------end--------------------");
	}
}

void CSocket::threadRecvProc(char* pMsgBuf)
{
}

//心跳包检测时间到，要检测心跳包是否超时的事宜，本函数只是把内存释放，子类应重写该函数以实现具体的逻辑
void CSocket::procPingTimeout(LPMsgHeader lpMsgHeader, time_t stCurrTime)
{
	CMemory* pMemory = CMemory::getInstance();
	pMemory->freeMemory(lpMsgHeader);
}

//epoll功能初始化，子进程中进行，本函数被CProcessCycle::workerProcessInit()调用
int CSocket::epollInit()
{
	//很多内核版本不处理epoll_create的参数，只要该参数>0即可
	//创建一个epoll对象，创建了一个红黑树，创建了一个双向链表
	m_nEpollHandle = epoll_create(m_nWorkerConnections);
	if (m_nEpollHandle == -1)
	{
		CLogger::logStderr(errno, "CSocket::epollInit()中epoll_create()失败。");
		exit(2);	//致命问题，直接退出
	}

	//(2)创建连接池
	initConnection();

	//(3)遍历所有监听socket[监听端口]，我们为每个监听增加一个连接池中的连接，说白了就是让一个socket和一个内存绑定，以方便记录该socket相关的数据、状态等等
	for (auto &item : m_vecListenSocket)
	{
		lp_connection_t pConn = getConnection(item->fd);

		if (pConn == NULL)
		{
			CLogger::logStderr(errno, "CSocket::epollInit()中getConnection()失败！");
			exit(2);		//致命问题，直接退出，资源由操作系统释放
		}
		pConn->listening = item;	//连接对象和监听对象关联，方便通过连接对象找监听对象
		item->connection = pConn;	//监听对象和连接对象关联，方便通过监听对象找连接对象

		//对监听端口的读事件设置处理方法，因为监听端口是用来等对方连接的发送三次握手的，所以监听端口关心的就是读事件
		pConn->rhandler = &CSocket::handleAccept;

		//往监听socket上增加事件，从而开始让监听端口履行其职责
		if (epollOperateEvent(item->fd,	//socket句柄
			EPOLL_CTL_ADD,				//事件类型，这里是增加
			EPOLLIN | EPOLLRDHUP,		//标志，EPOLLIN可读，EPOLLRDHUP：TCP连接的远端关闭或者半关闭
			0,
			pConn
			) == -1)
		{
			exit(2);	//由问题，直接退出，日志已经写过了
		}
	}

	return 1;
}

int CSocket::epollProcessEvents(int nTime)
{
	//等待事件，事件会返回到m_szEvents中，最多返回MAX_EVENTS个事件
	//如果两次调用epoll_wait()的时间间隔比较长，则可能在epoll的双向链表中积累了多个事件，所以调用epoll_wait可能取到多个事件
	//阻塞nTime这么长时间，除非（1）阻塞时间到达。（2）阻塞期间收到事件【比如新用户连入】会立刻返回。（3）调用时有事件。（4）来了信号，如kill -10 pid进行测试
	//如果nTime为-1，则一直阻塞，如果nTime为0则立即返回，即使没有任何事件
	//返回值：有错误则返回-1，错误在errno中，如果发个信号过来，就返回-1，错误信息为（4：Interrupted system call)
	//如果等待一段时间，并且超时了，则返回0
	//如果返回>0，则表示成功捕获到这么多事件（m_szEvents）
	int nEvents = epoll_wait(m_nEpollHandle, m_szEvents, MAX_EVENT, nTime);

	if (nEvents == -1)
	{
		//有错误发生，发送某个信号给本进程就可以导致该条件成立，而且错误码为4
		//EINTR错误的产生：当阻塞于某个慢系统调用的一个进程捕获到某个信号且相应的信号处理函数返回时，该系统调用可能返回一个EINTR错误
		if (errno == EINTR)
		{
			//信号所致，直接返回，一般不认为这是问题，打印日志记录，一般也不会人为给worker进程发送消息
			CLogger::logErrorCore(LOG_INFO, errno, "CSocket::epollProcessEvents()中epoll_wait()失败！");
			return 1;		//正常返回
		}
		else {
			//被认为是有问题，记录日志
			CLogger::logErrorCore(LOG_ALERT, errno, "CSocket::epollProcessEvents()中epoll_wait()失败！");
			return 0;	//非正常返回
		}
	}

	if (nEvents == 0)	//超时，并且没事件来
	{
		if (nTime != -1)
		{
			//要求epoll_wait阻塞一定时间，而不是一直阻塞，这属于阻塞时间到了，正常返回
			return 1;
		}
		//无限等待，不存在超时，却没有任何事件，属于不正常的情况，
		CLogger::logErrorCore(LOG_ALERT, 0, "CSocket::epollProcessEvents()中epoll_wait()没超时却没返回任何事件！");
		return 0;	//非正常返回
	}

	//会惊群，一个telnet上来，4个worker进程都会被惊动
	lp_connection_t pConn;
	uint32_t uEventType;
	for (size_t i = 0; i < nEvents; i++)
	{
		pConn = (lp_connection_t)(m_szEvents[i].data.ptr);
		uEventType = m_szEvents[i].events;	//取出事件类型

		if (uEventType & EPOLLIN)	//如果是读事件
        {
                                                //如果是新连接接入，这里应该是handleAccept
            (this->* (pConn->rhandler))(pConn); //如果是已经连入，这里应该是handleRead
		}

		if (uEventType & EPOLLOUT)	//如果是写事件，关闭连接也会触发这个
		{
			if (uEventType & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))	//客户端关闭，如果服务器端挂着一个写通知事件，则这个条件是可能成立的
			{
				//EPOLLERR：对应的连接发生错误
				//EPOLLHUP：对应的连接被挂起
				//EPOLLRDHUP：表示TCP连接的远端关闭或者半关闭连接
				//只有投递了写事件，当对端断开时，程序流程才走到这里，投递了写意味着iSendBufferExceptionCount标记肯定被+1了
				--pConn->iSendBufferExceptionCount;
			}
			else {
				(this->*(pConn->whandler))(pConn);	//如果数据没有发送完毕，则由系统驱动来发送，这里执行的是handleWrite
			}
		}
	}

	return 1;
}

//对epoll事件的具体操作，返回1表示成功
int CSocket::epollOperateEvent(int fd, uint32_t uEventType, uint32_t uFlag, int iFlagAction, lp_connection_t pConn)
{
	struct epoll_event ev;
	memset(&ev, 0, sizeof(ev));

	if (uEventType == EPOLL_CTL_ADD)	//往红黑树中增加节点
	{
		ev.events = uFlag;
		pConn->events = uFlag;
	}
	else if (uEventType == EPOLL_CTL_MOD)
	{
		//节点已在红黑树中，修改节点的事件信息
		ev.events = pConn->events;
		if (iFlagAction == 0)
		{
			//增加某个标记
			ev.events |= uFlag;
		}
		else if (iFlagAction == 1) {
			//去除某个标记
			ev.events &= ~uFlag;
		}
		else {
			//完全覆盖某个标记
			ev.events = uFlag;
		}
		pConn->events = ev.events;
	}
	else {
		//删除红黑树中节点，目前没这个需求
		return 1;
	}

	//原来的理解中，绑定ptr这个事，只在epoll_ctl_add的时候做一次即可，但是发现EPOLL_CTL_MOD似乎会破坏data.ptr，因此不管EPOLL_CTL_ADD还是EPOLL_CTL_MOD，都给重新赋值
	ev.data.ptr = (void*)pConn;

	if (epoll_ctl(m_nEpollHandle, uEventType, fd, &ev) == -1)
	{
		CLogger::logStderr(errno, "CSocket::epollOperateEvent()中epoll_ctl(%d, %ud, %ud, %d)失败。", fd, uEventType, uFlag, iFlagAction);
		return -1;
	}

	return 1;
}

//将一个待发送消息放到发消息队列中
void CSocket::msgSend(char* pSendBuf)
{
	CMemory* pMemory = CMemory::getInstance();
	CLock lock(&m_msgSendMutex);

	//发送消息队列过大可能给服务器带来风险
	if (m_iMsgSendCount > 50000)
	{
		//发送队列过大，比如客户端恶意不接受数据，就会导致队列越来越大
		//考虑为了服务器安全，干掉一些数据的发送，可能导致客户端出现问题，但总比服务器不稳定要好很多
		m_iDiscardSendPkgCount++;
		pMemory->freeMemory(pSendBuf);
		return;
	}

	//总体数据并无风险，不会导致服务器崩溃，要看看个体数据，找一下恶意者了
	LPMsgHeader pMsgHeader = (LPMsgHeader)pSendBuf;
	lp_connection_t pConn = pMsgHeader->pConn;
	if (pConn->iSendCount > 400)
	{
		//该用户收消息太慢【或者干脆不收消息】，累积的该用户的发送队列中有的数据条目数过大，认为是恶意用户，直接切断
		CLogger::logStderr(0, "CSocekt::msgSend()中发现某用户%d积压了大量待发送数据包，切断与他的连接！", pConn->fd);
		m_iDiscardSendPkgCount++;
		pMemory->freeMemory(pSendBuf);
		afterSocketClose(pConn);
		return;
	}
	
	++pConn->iSendCount;	//发送队列中有数据条目+1
	m_lstMsgSend.push_back(pSendBuf);
	++m_iMsgSendCount;

	//将信号量的值+1，这样其他卡在sem_wait的就可以走下去
	if (sem_post(&m_semMsgSend) == -1)
	{
		CLogger::logStderr(0, "CSocekt::msgSend()中sem_post(&m_semEventSendQueue)失败。");
	}
}

void CSocket::afterSocketClose(lp_connection_t pConn)
{
	if (m_bKickEnabled)
	{
		deleteFromTimerQueue(pConn);
	}
	if (pConn->fd != -1)
	{
		close(pConn->fd);
		pConn->fd = -1;
	}
	if (pConn->iSendBufferExceptionCount > 0)
	{
		--pConn->iSendBufferExceptionCount;
	}
	addToRecycleQueue(pConn);
}

//读取各项配置
void CSocket::readConf()
{
	CConfigParser* pConfig = CConfigParser::getInstance();
	m_nWorkerConnections = pConfig->getIntDefault("worker_connections", m_nWorkerConnections);
	m_nListenPortCount = pConfig->getIntDefault("ListenPortCount", m_nListenPortCount);
	m_nRecycleConnWaitTime = pConfig->getIntDefault("Sock_RecyConnectionWaitTime", m_nRecycleConnWaitTime);

	m_bKickEnabled = pConfig->getBoolDefault("Sock_WaitTimeEnable", 0);	//是否开启踢人时钟，1：开启   0：不开启
	m_iWaitTime = pConfig->getIntDefault("Sock_MaxWaitTime", m_iWaitTime);	//多少秒检测一次是否 心跳超时，只有当Sock_WaitTimeEnable = 1时，本项才有用
	m_iWaitTime = (m_iWaitTime > 5) ? m_iWaitTime : 5;
	m_bTimeoutKick = pConfig->getBoolDefault("Sock_TimeOutKick", 0);			//当时间到达Sock_MaxWaitTime指定的时间时，直接把客户端踢出去，只有当Sock_WaitTimeEnable = 1时，本项才有用 

	m_bFloodAttackTestEnabled = pConfig->getBoolDefault("Sock_FloodAttackKickEnable", 0);
	m_iFloodTimeInterval = pConfig->getIntDefault("Sock_FloodTimeInterval", 100);
	m_nFloodKickCount = pConfig->getIntDefault("Sock_FloodKickCounter", 10);
}

//监听端口，支持多个端口
bool CSocket::openListeningSockets()
{
	int sockfd;
	struct sockaddr_in servAddr;
	int nPort;
	char szTmp[100] = { 0 };

	//初始化相关
	memset(&servAddr, 0, sizeof(servAddr));	//先初始化一下
	servAddr.sin_family = AF_INET;			//选择协议族为IPV4
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);	//监听本地所有ip地址

	//中途用到的一些配置信息
	CConfigParser* pConfig = CConfigParser::getInstance();
	for (size_t i = 0; i < m_nListenPortCount; i++)
	{
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		if (sockfd == -1)
		{
			CLogger::logStderr(errno, "CSocket::openListeningSockets()中socket()失败，i = %d,", i);
			return false;
		}

		//setsockopt()：设置一些套接字选项
		//参数2表示级别
		//参数3表示运行重用地址 SO_REUSEADDR，注意解决TIME_WAIT导致bind失败的问题
		int reuseAddr = 1;
		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const void*)&reuseAddr, sizeof(reuseAddr)) == -1)
		{
			CLogger::logStderr(errno, "CSocket::openListeningSockets()中setsockopt(SO_REUSEADDR)失败，i = %d", i);
			close(sockfd);
			return false;
		}

		//为处理惊群问题使用reuseport
		int reuseport = 1;
		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const void*)&reuseAddr, sizeof(int)) == -1)
		{
			// 失败顶多是惊群
			CLogger::logStderr(errno, "CSocket::openListeningSockets()中setsockopt(SO_REUSEPORT)失败，i = %d", i);
		}

		// 设置该socket为非阻塞
		if (setNonBlocking(sockfd) == false)
		{
			CLogger::logStderr(errno, "CSocket::openListeningSockets()中setNonBlocking()失败，i = %d", i);
			close(sockfd);
			return false;
		}

		//设置本服务要监听的地址和端口，这样客户端才能连接到该地址和端口并发送数据
		szTmp[0] = 0;
		sprintf(szTmp, "ListenPort%d", i);
		nPort = pConfig->getIntDefault(szTmp, 10000);
		servAddr.sin_port = htons((in_port_t)nPort);

		//绑定服务器地址结构
		if (bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1)
		{
			CLogger::logStderr(errno, "CSocket::openListeningSockets()中bind()失败，i = %d。", i);
			close(sockfd);
			return false;
		}

		//开始监听
		if (listen(sockfd, LISTEN_BACKLOG) == -1)
		{
			CLogger::logStderr(errno, "CSocket::openListeningSockets()中listen()失败，i = %d。", i);
			close(sockfd);
			return false;
		}

		//操作成功，放到列表里来
		lp_listening_t pListenSocketItem = new listening_t;
		memset(pListenSocketItem, 0, sizeof(listening_t));
		pListenSocketItem->port = nPort;
		pListenSocketItem->fd = sockfd;
		CLogger::logErrorCore(LOG_INFO, 0, "监听%d端口成功！", nPort);

		m_vecListenSocket.push_back(pListenSocketItem);
	}

	if (m_vecListenSocket.size() <= 0)
	{
		return false;
	}

	return true;
}

void CSocket::closeListeningSockets()
{
	for (size_t i = 0; i < m_nListenPortCount; i++)
	{
		close(m_vecListenSocket[i]->fd);
		CLogger::logErrorCore(LOG_INFO, 0, "关闭监听端口%d！", m_vecListenSocket[i]->fd);
	}
}

bool CSocket::setNonBlocking(int sockfd)
{
	int val = 1;
	if (ioctl(sockfd, FIONBIO, &val) == -1)
	{
		return false;
	}

	return true;
}

//建立连接专用函数，当新连接进入时，本函数会被epollProcessEvents()所调用
void CSocket::handleAccept(lp_connection_t oldConn)
{
	//因为listen套接字上用的不是ET（边缘触发）而是LT（水平触发），意味着客户端连入如果我不处理，这个函数会被多次调用，所以我这里可以不必多次accept，可以只执行一次accept()
	//这也可以避免本函数被卡太久，注意本函数应该尽快返回，以免阻塞程序运行
	struct sockaddr remoteAddr;		//客户端的socket地址
	socklen_t sockLen;				
	int iErr;
	int iLevel;
	int clientFd;
	static int siUseAccept4 = 1;
	lp_connection_t newConn;

	sockLen = sizeof(remoteAddr);

	do {
		if (siUseAccept4)
		{
			clientFd = accept4(oldConn->fd, &remoteAddr, &sockLen, SOCK_NONBLOCK);
		}
		else {
			clientFd = accept(oldConn->fd, &remoteAddr, &sockLen);
		}
		
		if (clientFd == -1)
		{
			iErr = errno;
			if (iErr == EAGAIN)	//accept()没有准备好，EAGAIN和EWOULDBLOCK是一样的
			{
				//一般不会有这个错误，我们这里只取一个连接（accept()一次），除非用一个循环不断地accept()取走所有的连接
				return;
			}

			iLevel = LOG_ALERT;
			if (iErr == ECONNABORTED)	//ECONNABORTED错误发生在对方意外关闭套接字后，【您的主机中的软件放弃了一个已建立的连接，由于超时或者其他失败而终止连接（用户插拔网线就有可能出现这个错误）】
			{
				//该错误被描述为"software caused connection abort"，即“软件引起的连接终止”。原因在于当服务器和客户端完成3次握手后，
				//客户发送了一个RST（复位）分节，在服务器看来，就在该连接已由TCP排队，等着服务器调用accept的时候RST却到达了。
				//POSIX规定此时的errno值必须是ECONNABORTED。源自Berkeley的实现完全在内核中处理中止的连接，服务器将永远不指定该终止的发送。
				//服务器进程一般可以忽略该错误，直接再次调用accept
				iLevel = LOG_ERR;
			}
			else if (iErr == EMFILE || iErr == EMFILE) {	//EMFILE:进程的fd已用尽【已达到系统所允许单一进程所能打开的文件/套接字总数】
															//ulimit -n 看看文件描述符限制，如果是1024的话，需要改大；打开的文件句柄数过多，把系统的fd软现在和硬限制都抬高
				iLevel = LOG_CRIT;
			}

			if (siUseAccept4 && iErr == ENOSYS)	//accept4()函数没实现
			{
				siUseAccept4 = 0;	//标记不使用accept4()函数
				continue;			//回去重新用accept()
			}
			return;
		}

		//走到这里的，表示accept4()/accept()成功了
		if (m_iOnlineUserCount >= m_nWorkerConnections)		//用户连接过多要关闭该用户socket，因为现在也没分配连接，所以直接关闭即可
		{
			CLogger::logStderr(0,"超出系统允许的最大连入用户数(最大允许连入数%d)，关闭连入请求(%d)。", m_nWorkerConnections, clientFd);
			close(clientFd);
			return;
		}

		//如果某些恶意用户连上来发了1条数据就断，不断连接，会导致频繁调用getConnection()使服务器短时间内产生大量连接，危及服务器安全
		if (m_lstConnection.size() > (m_nWorkerConnections * 5))
		{
			//比如允许同时最大2048个连接，但连接池却有2048 * 5这么大容量，这肯定使表示短时间内产生大量连接/断开，因为我们的延迟回收机制，这里连接还在垃圾池里没有被回收
			if (m_lstFreeConnection.size() < m_nWorkerConnections)
			{
				//整个连接池这么大，而空闲连接却这么少，所以我认为短时间内产生大量连接，发一个包后就断开，
				//一直到m_lstFreeConnection变得足够大【连接池中被回收的足够多】
				close(clientFd);
				return;
			}
		}

		newConn = getConnection(clientFd);
		if (newConn == NULL)
		{
			//连接池中连接不够用，那么直接把socket关闭并返回
			if (close(clientFd) == -1)
			{
				CLogger::logErrorCore(LOG_ALERT, errno, "CSocekt::handleAccept()中close(%d)失败！", clientFd);
			}
			return;
		}

		//成功拿到了连接池中一个连接
		memcpy(&newConn->stSockaddr, &remoteAddr, sockLen);	//拷贝客户端地址到连接对象

		if (!siUseAccept4)
		{
			//如果不是用accept4()取得的socket，要设置为非阻塞
			if (setNonBlocking(clientFd) == false)
			{
				//设置非阻塞失败
				handleClose(newConn);
				return;
			}
		}
		newConn->listening = oldConn->listening;			//连接对象和监听对象关联，方便通过连接对象找监听对象【关联到监听端口】

		newConn->rhandler = &CSocket::handleRead;			//设置数据到来时的处理函数
		newConn->whandler = &CSocket::handleWrite;			//设置发送数据时的写处理函数

		//客户端应该主动发送第一次的数据，这里将读事件加入epoll监控
		if (epollOperateEvent(clientFd, //socket描述符
			EPOLL_CTL_ADD,				//事件类型，增加
			EPOLLIN | EPOLLRDHUP,		//要增加的标志，EPOLLIN：可读，EPOLLRDHUP：TCP连接的远端关闭或者半关闭
			0,							//对于事件类型为增加的不需要该参数
			newConn						//连接池中连接
		) == -1)
		{
			handleClose(newConn);		//关闭socket，这种可以立即回收这个连接，无需延迟，因为其上还没有数据收发，谈不到业务逻辑无需延迟
			return;						//直接返回
		}

		if (m_bKickEnabled)
		{
			addToTimerQueue(newConn);
		}
		++m_iOnlineUserCount;
		break;
	}while(1);
}

//读数据的业务逻辑，当连接上有数据到来时，本函数被epollProcessEvents调用
void CSocket::handleRead(lp_connection_t pConn)
{
	bool bFlood = false;	//是否为flood攻击
	//收包，注意第二和第三个参数，我们用的始终是这两个参数，我们必须保证pConn->lpRecvBuf指向正确的收包位置，
	//保证pConn->iRecvLen指向正确的收包长度
	ssize_t reco = recvProc(pConn, pConn->lpRecvBuf, pConn->iRecvLen);
	if (reco <= 0)
	{
		return;	//该处理的recvProc已经处理了，直接return
	}

	//走到这里，说明成功收到了一些字节(>0)，要开始判断收到了多少数据
	if (pConn->curStat == PKG_HD_INIT)	//连接建立起来时肯定是这个状态，因为在getConnection()中已经把curStat成员赋值成PKG_HD_INIT了
	{
		if (reco == m_iPkgHeaderLen)	//正好收到完整包头，这里拆解包头
		{
			recvPkgHeaderProc(pConn, bFlood);
		}
		else {
			//收到的包头不完整，我们不能预料到每个包的长度，也不能预料各种拆包、粘包情况，所以收到不完整包也是有可能的
			pConn->curStat = PKG_HD_RECVING;
			pConn->lpRecvBuf = pConn->lpRecvBuf + reco;
			pConn->iRecvLen = pConn->iRecvLen - reco;
		}
	}
	else if (pConn->curStat == PKG_HD_RECVING)	//接收包头中，包头不完整，继续接收中，这个条件才会成立
	{
		if (pConn->iRecvLen == reco)	//要求收到的长度和我们实际收到的长度相等
		{
			recvPkgHeaderProc(pConn, bFlood);
		}
		else {
			//包头还是没有收完整，继续收包头
			pConn->lpRecvBuf = pConn->lpRecvBuf + reco;
			pConn->iRecvLen = pConn->iRecvLen - reco;
		}
	}
	else if (pConn->curStat == PKG_BD_INIT)	//包头刚好收完，准备收包体
	{
		if (reco == pConn->iRecvLen)	//包体也刚好收完整了
		{
			//检查是否是flood攻击
			if (m_bFloodAttackTestEnabled)
			{
				bFlood = testFlood(pConn);
			}
			//一个完整的包处理
			recvPkgProc(pConn, bFlood);
		}
		else {
			//收到的长度小于要收的长度
			pConn->curStat = PKG_BD_RECVING;
			pConn->lpRecvBuf = pConn->lpRecvBuf + reco;
			pConn->iRecvLen = pConn->iRecvLen - reco;
		}
	}
	else if(pConn->curStat == PKG_BD_RECVING)	//接收包体中，包体不完整，继续接收中
	{
		if (pConn->iRecvLen == reco)	//包体收完整了
		{
			if (m_bFloodAttackTestEnabled)
			{
				bFlood = testFlood(pConn);
			}
			//一个完整的包处理
			recvPkgProc(pConn, bFlood);
		}
		else {
			//包体没收完整，继续收
			pConn->lpRecvBuf = pConn->lpRecvBuf + reco;
			pConn->iRecvLen = pConn->iRecvLen - reco;
		}
	}
	if (bFlood)
	{
		afterSocketClose(pConn);
	}

}

//设置发送数据时的写处理函数，当套接字可写时epoll通知我们，本函数被epollProcessEvents调用
//走到这里，数据就是没发送完毕，要继续发送
void CSocket::handleWrite(lp_connection_t pConn)
{
	CMemory* pMemory = CMemory::getInstance();

	ssize_t lSendSize = sendProc(pConn, pConn->lpSendBuffer, pConn->iSendLen);
	if (lSendSize > 0 && lSendSize != pConn->iSendLen)
	{
		//没有全部发送完毕，数据只发出去了一部分
		pConn->lpSendBuffer = pConn->lpSendBuffer + lSendSize;
		pConn->iSendLen = pConn->iSendLen - lSendSize;
		return;
	}
	else if (lSendSize == -1) {
		//不太可能出现，可以发送数据时通知我发送数据，发送时却通知我发送缓冲区已满
		CLogger::logStderr(errno, "CSocket::handleWrite()时if(lSendSize == -1)成立，很怪异！");
		return;
	}
	
	if (lSendSize > 0 && lSendSize == pConn->iSendLen)	//成功发送完毕，做个通知，并且把写事件移除
	{
		if (epollOperateEvent(pConn->fd,				//socket句柄 
							EPOLL_CTL_MOD,				//事件类型
							EPOLLOUT,					//标志
							1,							//0：增加，1：去掉，2：完全覆盖
							pConn						//连接池中的连接
							) == -1)
		{
			CLogger::logStderr(errno, "CSocket::handleWrite()中epollOperateEvent失败。");
		}
		CLogger::logStderr(0, "CSocket::handleWrite()中数据发送完毕。");
	}

	//能走下来的，要么数据发送完毕，要么对端断开，执行收尾工作
	if (sem_post(&m_semMsgSend) == -1)
	{
		CLogger::logStderr(0, "CSocket::handleWrite()中sem_post(&m_semMsgSend)失败。");
	}
	pMemory->freeMemory(pConn->lpSendMemPtr);	//释放内存
	pConn->lpSendMemPtr = NULL;
	--pConn->iSendBufferExceptionCount;
}

void CSocket::handleClose(lp_connection_t pConn)
{
	freeConnection(pConn);
	if (pConn->fd != -1)
	{
		close(pConn->fd);
		pConn->fd = -1;
	}
}

//接收数据专用函数，如果断线、错误之类的，直接释放连接池中连接，然后关闭socket
ssize_t CSocket::recvProc(lp_connection_t pConn, char* pBuff, ssize_t iBufLen)
{
	ssize_t n = recv(pConn->fd, pBuff, iBufLen, 0);
	if (n == 0)
	{
		//客户端关闭【应该是正常完成了4次挥手】，直接回收连接，关闭socket
		afterSocketClose(pConn);
		return -1;
	}
	//客户端没断，走这里
	if (n < 0)	//被认为是有错误发生
	{
		//EAGAIN和EWOULDBLOCK，一般来讲，ET模式下会出现这个错误，因为ET模式下只触发一次，需要不停的recv，肯定有一个时刻会收到这种错误
		//LT模式下是来事件才收，所以不会出现这种错误
		if (errno == EAGAIN)
		{
			CLogger::logStderr(errno, "CSocket::recvProc()中recv错误码为EAGAIN或EWOULDBLOCK。");
			return -1;
		}
		//被信号中断
		if (errno == EINTR)
		{
			CLogger::logStderr(errno, "CSocket::recvProc()中recv错误码为EINTR。");
			return -1;
		}
		if (errno == ECONNRESET)
		{
			//如果客户端没有正常关闭socket连接，却关闭了整个应用程序，则会产生这个错误
            CLogger::logStderr(errno, "CSocket::recvProc()中recv错误码为ECONNRESET。");
		}
		else {
			if (errno == EBADF)
			{
				//多线程，偶尔会干掉socket，所以不排除产生这个错误的可能性
				CLogger::logStderr(errno, "CSocket::recvProc()中recv错误码为EBADF。");
			}
			else {
				CLogger::logStderr(errno, "CSocket::recvProc()中recv错误码为其他错误码。");
			}
		}

		afterSocketClose(pConn);
		return -1;
	}
	//收到了有效数据
	return n;	//返回收到的字节数
}

//包头收完整后的处理
void CSocket::recvPkgHeaderProc(lp_connection_t pConn, bool& bFlood)
{
	CMemory* pMemory = CMemory::getInstance();
	LPPkgHeader pPkgHeader = (LPPkgHeader)(pConn->szDataHeadInfo);	//正好收到包体时，包头信息肯定在szDataHeadInfo里面

	unsigned short iPkgLen = ntohs(pPkgHeader->pkgLen);	//注意这里网络序转本机序，所有传输到网络上的2字节数据，都要用htons转网络序，
														//所有从网络上收到的2字节，都要用ntohs转成本机序
	//恶意包或者错误包的判断
	if (iPkgLen < m_iPkgHeaderLen)
	{
		//伪造包或者包错误，否则整个包长怎么可能比包头还小（包长=包头+包体，就算包体时0字节，那么iPkgLen == m_iPkgHeaderLen）
		//报文总长度 < 包头长度，认定为非法用户，废包
		//状态和接收位置都复原，这些值都有必要，因为可能在其他状态，比如PKG_HD_RECVING状态调用这个函数
		pConn->curStat = PKG_HD_INIT;
		pConn->lpRecvBuf = pConn->szDataHeadInfo;
		pConn->iRecvLen = m_iPkgHeaderLen;
	}
	else if (iPkgLen > (PKG_MAX_LENGTH - 1000))	//客户端发来包居然说包长度 > 29000，肯定是恶意包
	{
		//恶意包，太大，认定非法用户，废包
		//状态和接收位置都复原，这些值都有必要，因为可能在其他状态，比如PKG_HD_RECVING状态调用这个函数
		pConn->curStat = PKG_HD_INIT;
		pConn->lpRecvBuf = pConn->szDataHeadInfo;
		pConn->iRecvLen = m_iPkgHeaderLen;
	}
	else {
		//合法的包头，继续处理
		//现在要分配内存开始收包体，因为包体的长度并不是固定的，所以内存肯定要new出来
		char* pTmpBuffer = (char*)pMemory->allocMemory(m_iMsgHeaderLen + iPkgLen, false);	//分配内存=消息头长度+包头长度+包体长度，第二个参数给false，表示内存不需要memset
		pConn->lpRecvMemPtr = pTmpBuffer;	//内存起始指针

		//(1)先填写消息头内容
		LPMsgHeader pTmpMsgHeader = (LPMsgHeader)pTmpBuffer;
		pTmpMsgHeader->pConn = pConn;
		pTmpMsgHeader->uCurrSequence = pConn->iCurrSequence;	//收到包时连接池中连接序号记录到消息头中来，以备将来用
		
		//(2)再填写包头内容
		pTmpBuffer += m_iMsgHeaderLen;							//往后移，跳过消息头，指向包头
		memcpy(pTmpBuffer, pPkgHeader, m_iPkgHeaderLen);		//直接把收到的包头拷贝进来
		if (iPkgLen == m_iPkgHeaderLen)
		{
			//该报文只有包头无包体，允许一个包只有包头，没有包体
			//相当于收完整了，直接入消息队列，交给后续业务逻辑线程去处理
			if (m_bFloodAttackTestEnabled)
			{
				bFlood = testFlood(pConn);
			}
			//处理完整包
			recvPkgProc(pConn, bFlood);
		}
		else {
			//开始收包体
			pConn->curStat = PKG_BD_INIT;		//当前状态发生改变，包头刚好收完，准备收包体
			pConn->lpRecvBuf = pTmpBuffer + m_iPkgHeaderLen;	//pTmpBuffer指向包头，这里 + m_iPkgHeaderLen指向包体位置
			pConn->iRecvLen = iPkgLen - m_iPkgHeaderLen;		//iPkgLen是整个包（包头+包体）大小，-m_iPkgHeaderLen(包头) = 包体长度
		}
	}
}

//包体收完整后的处理，也就是一个完整包的处理
void CSocket::recvPkgProc(lp_connection_t pConn, bool& bFlood)
{
	//把这段内存放到消息队列中去
	if (!bFlood)
	{
		g_clThreadPool.inMsgRecvQueueAndSignal(pConn->lpRecvMemPtr);
	}
	else {
		//对于有攻击倾向的，先把他的包丢掉
		CMemory* pMemory = CMemory::getInstance();
		pMemory->freeMemory(pConn->lpRecvMemPtr);	//直接释放掉内存，不入消息队列
	}
	pConn->lpRecvMemPtr = NULL;
	pConn->curStat = PKG_HD_INIT;		//收包状态恢复为原始状态，为收下一个包做准备
	pConn->lpRecvBuf = pConn->szDataHeadInfo;	//设置好收包的位置
	pConn->iRecvLen = m_iPkgHeaderLen;
}

//清理TCP发送消息队列
void CSocket::clearMsgSendQueue()
{
	char* pTmpMem;
	CMemory* pMemory = CMemory::getInstance();
	while (!m_lstMsgSend.empty())
	{
		pTmpMem = m_lstMsgSend.front();
		m_lstMsgSend.pop_front();
		pMemory->freeMemory(pTmpMem);
	}
}

//发送数据专用函数，返回本次发送的字节数
//返回>0，成功发送了一些字节
ssize_t CSocket::sendProc(lp_connection_t pConn, char* lpBuff, ssize_t lSize)
{
	ssize_t n;
	for (; ; )
	{
		n = send(pConn->fd, lpBuff, lSize, 0);	//send()系统函数，最后一个参数，一般为0
		if (n > 0)	//成功发送了一些数据
		{
			//n == lSize，想发送多少都发送成功了，表示发送完毕了
			//n < lSize，没发送完毕，肯定是发送缓冲区满了，没必要重试发送，直接返回
			return n;
		}

		if (n == 0)
		{
			//send()返回0，一般recv()返回0表示断开，send()返回0表示超时，对方主动关闭了连接
			//连接断开，并不在send动作里处理诸如关闭socket这种动作，集中到recv那里处理，
			//连接断开，epoll会通知，并且recvProc()里面会处理
			return 0;
		}
		if (errno == EAGAIN)	//这东西应该等于EWOULDBLOCK
		{
			return -1;			//表示TCP内核发送缓冲区已满，这个不算错误
		}
		if (errno == EINTR)
		{
			//这个不算错误，收到某个信号
			//打个日志等for循环再重新send一次
			CLogger::logStderr(errno, "CSocket::sendProc()中send()失败！");
		}
		else {
			return -2;
		}
	}
}

//将socket绑定的地址转换为文本格式[根据参数1给定的信息，获取地址端口字符串，返回这个字符串的长度]
//参数sa，客户端的ip地址信息一般再这里
//参数port，为1表示要把端口信息放到组合成的字符串里，为0则不包含端口信息
//参数ipAddr，文本保存到这里
//参数len，文本的宽度在这里记录
size_t CSocket::getIPAndPort(sockaddr* sa, int port, u_char* ipAddr, size_t len)
{
	struct sockaddr_in* sin;
	u_char* p;
	switch (sa->sa_family)
	{
	case AF_INET:
		sin = (struct sockaddr_in*)sa;
		p = (u_char*)&sin->sin_addr;
		if (port)	//端口信息也组合到字符串里
		{
			p = CLogger::snprintf(ipAddr, len, "%ud.%ud.%ud.%ud:%d", p[0], p[1], p[2], p[3], ntohs(sin->sin_port));
		}
		else {		//不需要组合端口信息到字符串
			p = CLogger::snprintf(ipAddr, len, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
		}
		return p - ipAddr;
		break;
	default:
		return 0;
		break;
	}
	return 0;
}

connection_s::connection_s()
{
	iCurrSequence = 0;
	pthread_mutex_init(&stLogicMutex, NULL);
}

connection_s::~connection_s()
{
	pthread_mutex_destroy(&stLogicMutex);
}

//分配出去一个连接的时候初始化一些内容
void connection_s::getOneToUse()
{
	++iCurrSequence;		
	fd = -1;
	curStat = PKG_HD_INIT;			//收包状态处于初始状态，准备接收数据包头【状态机】
	lpRecvBuf = szDataHeadInfo;		//要先收包头，所以收数据的buff直接就是szDataHeadInfo
	iRecvLen = sizeof(PkgHeader);	//指定收数据的长度，先要求收包头这么多字节的数据

	lpRecvMemPtr = NULL;			
	iSendBufferExceptionCount = 0;
	lpSendMemPtr = NULL;			
	events = 0;
	lLastPingTime = time(NULL);

	uLastFloodKickTime = 0;
	iFloodAttackCount = 0;
	iSendCount = 0;
}

//回收回来一个连接时做一些事
void connection_s::putOneToFree()
{
	++iCurrSequence;

	if (lpRecvMemPtr != NULL)	//我们曾经给这个连接分配过接收数据的内存，现在释放内存
	{
		CMemory::getInstance()->freeMemory((void *)lpRecvMemPtr);
		lpRecvMemPtr = NULL;
	}
	if (lpSendMemPtr != NULL)
	{
		CMemory::getInstance()->freeMemory((void*)lpSendMemPtr);
	}
}

//创建连接池
void CSocket::initConnection()
{
	lp_connection_t pConn;
	CMemory* pMemory = CMemory::getInstance();

	int iConnPoolLen = sizeof(connection_t);
	for (size_t i = 0; i < m_nWorkerConnections; i++)	//先创建这么多连接，后续不够再增加
	{
		pConn = (lp_connection_t)pMemory->allocMemory(iConnPoolLen, true);	//分配内存
		//手工调用构造函数，allocMemory无法调用构造
		pConn = new(pConn)connection_t();	//placement new,释放则显式调用pConn->~connection_t();
		pConn->getOneToUse();
		m_lstConnection.push_back(pConn);
		m_lstFreeConnection.push_back(pConn);
	}
	m_nFreeConnection = m_nTotalConnection = m_lstConnection.size();
}

//最终回收连接池，释放内存
void CSocket::clearConnection()
{
	lp_connection_t pConn;
	CMemory* pMemory = CMemory::getInstance();
	while (!m_lstConnection.empty())
	{
		pConn = m_lstConnection.front();
		m_lstConnection.pop_front();
		pConn->~connection_s();	//手工调用析构函数
		pMemory->freeMemory(pConn);
	}
}

//从连接池中获取一个空闲连接[当一个客户端连接tcp进入，我希望把这个连接和我的连接池中的一个连接对象绑到一起，后续，我可以通过这个连接，把这个对象拿到，因为对象里边可以记录各种信息]
lp_connection_t CSocket::getConnection(int sockfd)
{
	//因为可能有其他线程要访问m_lstFreeConnection，m_lstConnection，比如可能有专门的释放线程要释放或者主线程要释放之类的，所以要临界一下
	CLock lock(&m_connectionMutex);
	if (!m_lstFreeConnection.empty())
	{
		//有空闲的，自然是从空闲的摘取
		lp_connection_t pConn = m_lstFreeConnection.front();	//返回第一个元素但不检查元素存在与否
		m_lstFreeConnection.pop_front();						//移除第一个元素但不返回
		pConn->getOneToUse();								
		--m_nFreeConnection;
		pConn->fd = sockfd;
		return pConn;
	}

	//走到这里，表示没有空闲的连接了，那就考虑重新创建一个连接
	CMemory* pMemory = CMemory::getInstance();
	lp_connection_t pConn = (lp_connection_t)pMemory->allocMemory(sizeof(connection_t), true);
	pConn = new(pConn)connection_t();
	pConn->getOneToUse();
	m_lstConnection.push_back(pConn);	//入到总表中来，但不能入到空闲表中来，因为凡是调用这个函数的，肯定是要用这个连接的
	++m_nTotalConnection;
	pConn->fd = sockfd;
	return pConn;
}

void CSocket::freeConnection(lp_connection_t pConn)
{
	CLock lock(&m_connectionMutex);
	pConn->putOneToFree();

	//扔到空闲连接列表里
	m_lstFreeConnection.push_back(pConn);

	++m_nFreeConnection;
}

//将要回收的连接放到一个队列中来，后续有专门的线程会处理这个队列中的连接的回收
//有些连接，我们不希望马上释放，要隔一段时间后再释放以确保服务器的稳定，所以，我们把这种隔一段时间才释放的连接先放到一个队列中来
void CSocket::addToRecycleQueue(lp_connection_t pConn)
{
	std::list<lp_connection_t>::iterator pos;
	bool bFind = false;
	CLock lock(&m_recycleMutex);	//针对连接回收 列表的互斥量
	
	//如下判断防止连接被多次扔到回收站中来
	for (pos = m_lstRecycleConnection.begin(); pos != m_lstRecycleConnection.end(); ++pos)
	{
		if ((*pos) == pConn)
		{
			bFind = true;
			return;
		}
	}

	pConn->lRecycleTime = time(NULL);
	++pConn->iCurrSequence;
	m_lstRecycleConnection.push_back(pConn);
	++m_nTotalRecycleConnection;
	--m_iOnlineUserCount;
}

void CSocket::addToTimerQueue(lp_connection_t pConn)
{
	CMemory* pMemory = CMemory::getInstance();
	time_t lFutureTime = time(NULL);
	lFutureTime += m_iWaitTime;

	CLock lock(&m_timerMutex);	//互斥，因为要操作m_timeQueueMap了
	LPMsgHeader pHeader = (LPMsgHeader)pMemory->allocMemory(m_iMsgHeaderLen, false);
	pHeader->pConn = pConn;
	pHeader->uCurrSequence = pConn->iCurrSequence;
	m_mapTimer.insert(std::make_pair(lFutureTime, pHeader));
	m_iTimerMapSize++;
	m_lFirstTimer = getEarliestTime();
}

time_t CSocket::getEarliestTime()
{
	//取第一个元素的值
	return m_mapTimer.begin()->first;
}

LPMsgHeader CSocket::revmoveFirstTimer()
{
	std::multimap<time_t, LPMsgHeader>::iterator pos;
	LPMsgHeader pHeader;
	if (m_iTimerMapSize <= 0)
	{
		return NULL;
	}
	pos = m_mapTimer.begin();
	pHeader = pos->second;
	m_mapTimer.erase(pos);
	--m_iTimerMapSize;
	return pHeader;
}

//根据给的当前时间，从m_mapTimer中找到比这个时间更早的1个节点返回去，这些节点都是时间超过了的，要处理的节点
LPMsgHeader CSocket::getOverTimeTimer(time_t currTime)
{
	CMemory* pMemory = CMemory::getInstance();
	LPMsgHeader pHeader;
	if (m_iTimerMapSize <= 0 || m_mapTimer.empty())
	{
		return NULL;
	}

	time_t lEarlistTime = getEarliestTime();
	if (lEarlistTime < currTime)	//定时器到期时间小于当前时间，表示已经到期了
	{
		//这回确实时有到时间的了【超时节点】
		pHeader = revmoveFirstTimer();	//将超时的节点从m_mapTimer中删除，并把该节点的第二项返回

		if (!m_bTimeoutKick)	//超时不踢出
		{
			//如果没有设置超时踢出，才进到这里来
			//下次超时依然要判断，把这个节点加回来
			time_t lNewInQueTime = currTime + m_iWaitTime;
			LPMsgHeader pNewHeader = (LPMsgHeader)pMemory->allocMemory(sizeof(MsgHeader), false);
			pNewHeader->pConn = pHeader->pConn;
			pNewHeader->uCurrSequence = pHeader->uCurrSequence;
			m_mapTimer.insert(std::make_pair(lNewInQueTime, pNewHeader));	//自动排序 小->大
			m_iTimerMapSize++;
		}

		if (m_iTimerMapSize > 0)
		{
			m_lFirstTimer = getEarliestTime();	//计时器队列头部时间值保存到m_lFirstTimer，重新设置最早到期时间
		}
		return pHeader;
	}

	return NULL;
}

void CSocket::deleteFromTimerQueue(lp_connection_t pConn)
{
	std::multimap<time_t, LPMsgHeader>::iterator pos, posEnd;
	CMemory* pMemory = CMemory::getInstance();
	CLock lock(&m_timerMutex);
lblDel:
	pos = m_mapTimer.begin();
	posEnd = m_mapTimer.end();
	for (; pos != posEnd; ++pos)
	{
		if (pos->second->pConn == pConn)
		{
			pMemory->freeMemory(pos->second);
			m_mapTimer.erase(pos);
			--m_iTimerMapSize;
			goto lblDel;
		}
	}
	if (m_iTimerMapSize > 0)
	{
		m_lFirstTimer = getEarliestTime();
	}
}

void CSocket::clearAllFromTimerQueue()
{
	std::multimap<time_t, LPMsgHeader>::iterator pos, posEnd;
	CMemory* pMemory = CMemory::getInstance();
	pos = m_mapTimer.begin();
	posEnd = m_mapTimer.end();
	for (; pos != posEnd; ++pos)
	{
		pMemory->freeMemory(pos->second);
		--m_iTimerMapSize;
	}
	m_mapTimer.clear();
}

//测试释放flood攻击成立，成立则返回true，否则返回false
bool CSocket::testFlood(lp_connection_t pConn)
{
	struct timeval stCurrTime;		//当前时间
	uint64_t uCurrTimestamp;				//当前时间（毫秒)
	bool bKick = false;				//踢人标志

	//取得当前时间
	gettimeofday(&stCurrTime, NULL);
	uCurrTimestamp = (stCurrTime.tv_sec * 1000 + stCurrTime.tv_usec / 1000); //毫秒
	if ((uCurrTimestamp - pConn->uLastFloodKickTime) < m_iFloodTimeInterval)	//两次收包时间小于100毫秒
	{
		//发包太频繁记录
		pConn->iFloodAttackCount++;
		pConn->uLastFloodKickTime = uCurrTimestamp;
	}
	else {
		pConn->iFloodAttackCount = 0;
		pConn->uLastFloodKickTime = uCurrTimestamp;
	}

	if (pConn->iFloodAttackCount >= m_nFloodKickCount)
	{
		bKick = true;
	}

	return bKick;
}

void* CSocket::sendDataThread(void* pData)
{
	ThreadItem* pItem = static_cast<ThreadItem*>(pData);
	CSocket* pSock = pItem->m_pThis;
	int iErr;
	std::list<char*>::iterator pos, pos2, posEnd;

	char* pMsgBuf;
	LPMsgHeader pMsgHeader;
	LPPkgHeader pPkgHeader;
	lp_connection_t pConn;
	unsigned short iTmp;
	ssize_t lSendSize;

	CMemory* pMemory = CMemory::getInstance();
	while (CProcessAttribute::m_snStopFlag == 0)	//不退出
	{
		//如果信号量值>0，则-1并走下去，否则卡在这里【为了让信号量值+1，可以在其他线程调用sem_post，实际上在CSocket::msgSend()调用sem_post就达到了让这里sem_wait走下去的目的】
		//如果被某个信号中断，sem_wait也可能过早地返回，错误为EINTR
		//整个程序退出之前，也要sem_post一下，确保如果本线程卡在sem_wait，也能走下去从而让本线程成功返回
		if (sem_wait(&pSock->m_semMsgSend) == -1)
		{
			//失败？及时报告
			if (errno != EINTR)
			{
				CLogger::logStderr(errno, "CSocket::sendDataThread()中sem_wait(&pSock->m_semMsgSend)失败！");
			}
		}

		//一般走到这里表示需要处理数据收发了
		if (CProcessAttribute::m_snStopFlag != 0)	//进程要退出
		{
			break;
		}

		if (pSock->m_iMsgSendCount > 0)
		{
			iErr = pthread_mutex_lock(&pSock->m_msgSendMutex);	//因为我们要操作发送消息队列
			if (iErr != 0)
			{
				CLogger::logStderr(iErr, "CSocket::sendDataThread()中pthread_mutex_lock()失败，返回的错误码为%d！", iErr);
			}
			pos = pSock->m_lstMsgSend.begin();
			posEnd = pSock->m_lstMsgSend.end();
			while (pos != posEnd)
			{
				pMsgBuf = (*pos);	//拿到的每个消息都是消息头+包头+包体
				pMsgHeader = (LPMsgHeader)pMsgBuf;	//指向消息头
				pPkgHeader = (LPPkgHeader)(pMsgBuf + pSock->m_iMsgHeaderLen);	//指向包头
				pConn = pMsgHeader->pConn;

				//包过期，如果连接被回收，比如在handleClose()，addToRecycleQueue()中都会自增iCurrSequence
				//没有必要针对本连接来用m_connectionMutex来临界，只要下面条件成立，肯定是客户端连接已断开，要发送的数据肯定不需要发送了
				if (pConn->iCurrSequence != pMsgHeader->uCurrSequence)
				{
					//本包中保存的序号与pConn【连接池中连接】中实际序号已经不同，丢弃此消息，小心处理该消息的删除
					pos2 = pos;
					pos++;
					pSock->m_lstMsgSend.erase(pos2);
					--pSock->m_iMsgSendCount;
					pMemory->freeMemory(pMsgBuf);
					continue;
				}

				if (pConn->iSendBufferExceptionCount > 0)
				{
					//靠系统驱动来发送消息，所以这里不能再发送
					pos++;
					continue;
				}
				--pConn->iSendCount;	//发送队列中有的数据条目-1

				//走到这里，可以发送消息，一些必须的信息记录，要发送的也要从发送队列中干掉
				pConn->lpSendMemPtr = pMsgBuf;		//发送后释放用的，因为这段内存是new出来的
				pos2 = pos;
				pos++;
				pSock->m_lstMsgSend.erase(pos2);
				--pSock->m_iMsgSendCount;			//发送消息队列容量少1
				pConn->lpSendBuffer = (char*)pPkgHeader;		//要发送的数据的缓冲区指针，因为发送数据不一定全部都能发送出去，我们要记录数据发送到了哪里，需要知道下次数据从哪里开始发送
				iTmp = ntohs(pPkgHeader->pkgLen);		//包头+包体长度，打包时用了htons[本机序转网络序]，所以这里为了得到该数值，用了ntohs[网络序转本机序]
				pConn->iSendLen = iTmp;					//要发送多少数据，因为发送数据不一定全部都能发送出去，我们需要知道剩余有多少数据还没发送

				//我们采用epoll水平触发的策略，能走到这里的，都应该时还没有投递写事件到epoll中
				//epoll水平触发发送数据的改进方案:
				//开始不把socket写事件通知加入到epoll，当我们需要写数据的时候，直接调用write/send发送数据
				//如果返回了EAGIN[发送缓冲区满了，需要等待可写事件才能继续往缓冲区里写数据]，此时我再把写事件通知加入到epoll
				//此时，就变成了在epoll驱动下写数据，全部数据发送完毕后，再把写事件通知从epoll中移除
				//优点：数据不多的时候，可以避免epoll的写事件的增加/删除，提高了程序的执行效率
				//(1)先直接调用write或send发送数据

                lSendSize = pSock->sendProc(pConn, pConn->lpSendBuffer, pConn->iSendLen);
				if (lSendSize > 0)
				{
					if (lSendSize == pConn->iSendLen)
					{
						//成功发送的和要求发送的数据量相等，说明全部发送成功了（数据全部发送完毕）
						pMemory->freeMemory(pConn->lpSendMemPtr);	//释放内存
						pConn->lpSendMemPtr = NULL;
						pConn->iSendBufferExceptionCount = 0;	//这行其实可以没有
					}
					else {	//没有全部发送完毕，数据只发送出去了一部分，但肯定时因为发送缓冲区满了
						//发送到哪里剩余多少，记录下来，方便下次sendProc()时使用
						pConn->lpSendBuffer = pConn->lpSendBuffer + lSendSize;
						pConn->iSendLen = pConn->iSendLen - lSendSize;

						//因为发送缓冲区满了，所以现在要依赖系统通知来发送数据了
						++pConn->iSendBufferExceptionCount;	//标记发送缓冲区满了，需要通过epoll事件来驱动消息的继续发送【原子+1】，不可写成
															//pConn->iSendBufferExceptionCount = pConn->iSendBufferExceptionCount + 1
						//投递此事件后，我们将依靠epoll驱动调用handleWrite函数发送数据
						if (pSock->epollOperateEvent(
							pConn->fd,		//socket句柄
							EPOLL_CTL_MOD,	//事件类型，这里是修改
							EPOLLOUT,		//EPOLLOUT：可写
							0,				//0：增加，1：去掉，2：覆盖
							pConn) == -1)
						{
							CLogger::logStderr(errno, "CSocket::sendDataThread()中epollOperateEvent失败。");
						}
					}
					continue;
				}
				else if(lSendSize == 0) {
					//发送0个字节，首先因为我发送的内容不是0个字节的
					//缓冲区满返回的应该是-1，错误码应该是EAGIN，这种情况下，把包丢弃（按对端关闭socket处理）
					//打印日志，看看是否有这种情况发生
					pMemory->freeMemory(pConn->lpSendMemPtr);	//释放内存
					pConn->lpSendMemPtr = NULL;
					pConn->iSendBufferExceptionCount = 0;
					continue;
				}
				else if (lSendSize == -1) {
					//发送缓冲区已满（一个字节都没有发送出去，说明发送缓冲区当前正好是满的）
					++pConn->iSendBufferExceptionCount;	//标记发送缓冲区已满，需要通过epoll事件来驱动消息的继续发送
					//投递此事件后，我们将依靠epoll驱动调用handleWrite函数发送数据
					if (pSock->epollOperateEvent(
						pConn->fd,		//socket句柄
						EPOLL_CTL_MOD,	//事件类型，这里是修改
						EPOLLOUT,		//EPOLLOUT：可写
						0,				//0：增加，1：去掉，2：覆盖
						pConn) == -1)
					{
						CLogger::logStderr(errno, "CSocket::sendDataThread()中epollOperateEvent2失败。");
					}
					continue;
				}
				else {
					//能走到这里应该是返回值为-2了，一般就认为是对端断开了，等待recv()来做断开socket以及资源回收
					pMemory->freeMemory(pConn->lpSendMemPtr);	//释放内存
					pConn->lpSendMemPtr = NULL;
					pConn->iSendBufferExceptionCount = 0;
					continue;
				}
			}

			iErr = pthread_mutex_unlock(&pSock->m_msgSendMutex);
			if (iErr != 0)
			{
				CLogger::logStderr(iErr, "CSocket::sendDataThread()中pthread_mutex_unlock()失败，返回的错误码为%d！", iErr);
			}
		}
	}

	return nullptr;
}

void* CSocket::recycleConnectionThread(void* pData)
{
	ThreadItem* pItem = static_cast<ThreadItem*>(pData);
	CSocket* pSock = pItem->m_pThis;
	time_t lCurrTime;
	int iErr;
	std::list<lp_connection_t>::iterator pos, posEnd;
	lp_connection_t pConn;
	while (1) {
		//每次休息200毫秒
		usleep(200 * 1000);	//单位是微秒
		//不管啥情况，先把这个条件成立时该做的动作做了
		if (pSock->m_nTotalRecycleConnection > 0)
		{
			lCurrTime = time(NULL);
			iErr = pthread_mutex_lock(&pSock->m_recycleMutex);
			if (iErr != 0)
			{
				CLogger::logStderr(iErr, "CSocket::recycleConnectionThread()中pthread_mutex_lock()失败，返回的错误码为%d！", iErr);
			}
		lblConnRecyle:
			pos = pSock->m_lstRecycleConnection.begin();
			posEnd = pSock->m_lstRecycleConnection.end();
			for (; pos!=posEnd; ++pos)
			{
				pConn = (*pos);
				if ((pConn->lRecycleTime + pSock->m_nRecycleConnWaitTime) > lCurrTime && CProcessAttribute::m_snStopFlag == 0)
				{
					continue;		//没到释放的时间
				}

				//到释放的时间了
				if (pConn->iSendBufferExceptionCount > 0)
				{
					//打印日志
					CLogger::logStderr(0, "CSocekt::recycleConnectionThread()中到释放时间却发现p_Conn.iSendBufferFullCount!=0，这个不该发生");
				}

				//流程走到这里，表示可以释放，那我们就开始释放
                --pSock->m_nTotalRecycleConnection;		//待释放连接队列大小-1
				pSock->m_lstRecycleConnection.erase(pos);
				pSock->freeConnection(pConn);
				goto lblConnRecyle;
			}

			iErr = pthread_mutex_unlock(&pSock->m_recycleMutex);
			if (iErr != 0)
			{
				CLogger::logStderr(iErr, "CSocekt::recycleConnectionThread()pthread_mutex_unlock()失败，返回的错误码为%d！", iErr);
			}
		}

		if (CProcessAttribute::m_snStopFlag == 1)	//要退出整个程序
		{
			if (pSock->m_nTotalConnection > 0)
			{
				//因为要退出，就得强制性释放了，不管到没到时间
				iErr = pthread_mutex_lock(&pSock->m_recycleMutex);
				if (iErr != 0)
				{
					CLogger::logStderr(iErr, "CSocekt::recycleConnectionThread()中pthread_mutex_lock(&pSock->m_recycleMutex)失败，返回的错误码为%d！", iErr);
				}
			lblConnRecyleForce:
				pos = pSock->m_lstRecycleConnection.begin();
				posEnd = pSock->m_lstRecycleConnection.end();
				for (; pos != posEnd; ++pos)
				{
					pConn = (*pos);
					
					--pSock->m_nTotalConnection;		//待释放连接队列大小-1
					pSock->m_lstRecycleConnection.erase(pos);
					pSock->freeConnection(pConn);
					goto lblConnRecyleForce;
				}

				iErr = pthread_mutex_unlock(&pSock->m_recycleMutex);
				if (iErr != 0)
				{
					CLogger::logStderr(iErr, "CSocekt::recycleConnectionThread()中pthread_mutex_unlock(&pSock->m_recycleMutex)失败，返回的错误码为%d！", iErr);
				}
			}
			break;
		}
	}

	return nullptr;
}

void* CSocket::timerQueueMonitorThread(void* pData)
{
	ThreadItem* pItem = static_cast<ThreadItem*>(pData);
	CSocket* pSock = pItem->m_pThis;
	time_t lAbsoluteTime, lCurrTime;
	int iErr;

	while (CProcessAttribute::m_snStopFlag == 0)	//程序不退出
	{
		if (pSock->m_iTimerMapSize > 0)	//队列不为空
		{
			//时间队列中最近发生的事情放到lAbsoluteTime里面
			lAbsoluteTime = pSock->m_lFirstTimer;
			lCurrTime = time(NULL);
			if (lAbsoluteTime < lCurrTime)
			{
				//时间到了，可以处理了
                std::list<LPMsgHeader> lstOverTime;		//到期定时器列表
				LPMsgHeader pResult;
				iErr = pthread_mutex_lock(&pSock->m_timerMutex);
				if (iErr != 0)
				{
					CLogger::logStderr(iErr, "CSocket::timerQueueMonitorThread()中pthread_mutex_lock()失败，返回的错误码为%d！", iErr);
				}

				while ((pResult = pSock->getOverTimeTimer(lCurrTime)) != NULL)	//一次性把所有的超时节点都拿过来
				{
                    lstOverTime.push_back(pResult);
				}

				iErr = pthread_mutex_unlock(&pSock->m_timerMutex);
				if (iErr != 0)
				{
					CLogger::logStderr(iErr, "CSocket::timerQueueMonitorThread()中pthread_mutex_unlock()失败，返回的错误码为%d！", iErr);
				}

				LPMsgHeader pTmp;
                while (!lstOverTime.empty())
				{
                    pTmp = lstOverTime.front();
                    lstOverTime.pop_front();
					pSock->procPingTimeout(pTmp, lCurrTime);
				}
			}
		}
		usleep(500 * 1000);
	}

	return nullptr;
}
